#include <pcap.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <ctype.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>


#include "ethernet.h"
#include "printutil.h"
#include "packetparser.h"
#include "usagetracker.h"
#include "main.h"


/* default snap length (maximum bytes per packet to capture) */
#define SNAP_LEN 65535

using namespace std;

/* Get MS */
long long getmsofday()
{
   struct timeval tv;
   struct timezone tz;
   gettimeofday(&tv, &tz);
   return (long long)tv.tv_sec*1000 + tv.tv_usec/1000;
}

/* print help text */
void print_app_usage(void) {
	printf("Usage: ./trafficmonitor [interface]\n");
	printf("\n");
	printf("Options:\n");
	printf("    interface    Listen on <interface> for packets.\n");
	printf("\n");
	return;
}

/* dissect/print packet */
void got_packet(u_char *args, const struct pcap_pkthdr *header, const u_char *packet) {
	PacketParser mypacket(args, header, packet);
	
	if (mypacket.IsTCP){
		UsageTracker::AddEntry(mypacket.DestPort, mypacket.IP, mypacket.Size);
	}
}

vector<UsageInfo> GetUsageInformation(string Device = "", int num_packets = 100) {
	const char* dev = NULL;			/* capture device name */
	char errbuf[PCAP_ERRBUF_SIZE];		/* error buffer */
	pcap_t *handle;				/* packet capture handle */

	char filter_exp[] = "ip";		/* filter expression [3] */
	struct bpf_program fp;			/* compiled filter program (expression) */
	bpf_u_int32 mask;			/* subnet mask */
	bpf_u_int32 net;			/* ip */

	/* check for capture device name on command-line */
	if (Device == "") {
		/* find a capture device if not specified on command-line */
		dev = pcap_lookupdev(errbuf);
		if (dev == NULL) {
			fprintf(stderr, "Couldn't find default device: %s\n", errbuf);
			fprintf(stderr, "Try running as sudo.\n");
			exit(EXIT_FAILURE);
		}
	} else {
		dev = Device.c_str();
	}
	
	/* get network number and mask associated with capture device */
	if (pcap_lookupnet(dev, &net, &mask, errbuf) == -1) {
		fprintf(stderr, "Couldn't get netmask for device %s: %s\n",
		    dev, errbuf);
		net = 0;
		mask = 0;
	}

	/* print capture info */
	//printf("Device: %s\n", dev);

	/* open capture device */
	handle = pcap_open_live(dev, SNAP_LEN, 1, 1000, errbuf);
	if (handle == NULL) {
		fprintf(stderr, "Couldn't open device %s: %s\n", dev, errbuf);
		exit(EXIT_FAILURE);
	}

	/* make sure we're capturing on an Ethernet device [2] */
	if (pcap_datalink(handle) != DLT_EN10MB) {
		fprintf(stderr, "%s is not an Ethernet\n", dev);
		exit(EXIT_FAILURE);
	}

	/* compile the filter expression */
	if (pcap_compile(handle, &fp, filter_exp, 0, net) == -1) {
		fprintf(stderr, "Couldn't parse filter %s: %s\n",
		    filter_exp, pcap_geterr(handle));
		exit(EXIT_FAILURE);
	}

	/* apply the compiled filter */
	if (pcap_setfilter(handle, &fp) == -1) {
		fprintf(stderr, "Couldn't install filter %s: %s\n",
		    filter_exp, pcap_geterr(handle));
		exit(EXIT_FAILURE);
	}

	vector<UsageInfo> ret;
	//while (true)
	{
		/* Start Clock */
		UsageTracker::Clear();
		long long begin = getmsofday();
		
		/* Wait for packet(s) */
		pcap_loop(handle, num_packets, got_packet, NULL);
		
		/* Stop Clock */
		long long end = getmsofday();
		double elapsed_secs = (double)(end - begin) / 1000.0; //1000ms per second
		if (elapsed_secs != 0){
			for (int i = 0; i < UsageTracker::Entries.size(); i++){
				double Size = UsageTracker::Entries[i].TotalSize / 1024.0;
				
				UsageInfo info = UsageTracker::Entries[i];
				info.Speed = Size/elapsed_secs;
				ret.push_back(info);
			}			
		}
	}

	/* cleanup */
	pcap_freecode(&fp);
	pcap_close(handle);

	return ret;
}

/*int main(int argc, char **argv){
	while (true){
		sleep(2);
		vector<UsageInfo> Items = GetUsageInformation("eth1", 1000);
		system("clear");
		for (int i = 0; i < Items.size(); i++){
			printf("%s \t%i \t%.2fkbps\n", Items[i].IP.c_str(), Items[i].Port, Items[i].Speed);
		}
	}
}*/
